NKOJ-3776 工资管理
P3776工資管理
時間限制 : - MS 空間限制 : 165536 KB
評測說明 : 1000ms
問題描述
輸入格式
第一行包含兩個正整數n,m,分別表示員工的人數和操作次數。 接下來m行,每行一個操作,形式如題面所述。輸出格式
包含若干行,對于每個減工資操作,若可行,輸出TAK,否則輸出NIE。樣例輸入 1
3 8 U 1 5 U 2 7 Z 2 6 U 3 1 Z 2 6 U 2 2 Z 2 6 Z 2 1樣例輸出 1
NIE TAK NIE TAK樣例輸入 2
13 17 U 1 12 Z 1 9 Z 1 5 Z 4 7 U 7 18 Z 1 1 Z 1 8 U 6 4 U 1 9 U 3 13 Z 5 2 U 7 8 U 4 20 U 7 14 Z 6 1 Z 3 2 Z 8 7樣例輸出 2
TAK TAK NIE TAK TAK NIE NIE TAK NIE提示
對于30%的數據:1<=n,m<=1000 對于100%的數據:1<=n,m<=200000 1<=x<=n,0<=y<=10^9,1<=y<=10^9。來源 改編自POI2015 Logistyka
沒想到老板的公司竟然有高達20萬個員工
題解
思路
分析下題意
對于這道題目,我們大概可以分析出
當扣錢次數為y次時,一個人最多被扣掉y塊錢 所以對于工資>y的人,他最多也就只能被扣y塊錢(最多被扣y次) 對于工資<=y時,他最多就被扣完而且對于一個扣款y,因為它是分y次扣的,所以可以被扣在多個人頭上(這個結論應該是很容易想到的,不作證明)
所以我們就可以分析出結果
對于工資大于y的人,我們記錄他被扣的錢為y元 而工資小于y的人,我們就把他的錢扣完舉例
假設修改后工資為 1 2 5 7 8 6 3 2 1如果老板的要求是,5個人,扣4次 所以一共是扣20塊錢 但由于5 7 8 6 大于4,所以這四個都算作4==>和為16 而剩下的,可以由1,2,3(工資額)一起分擔 所以,根據上面的結果來看,這個情況就是4*4(工資大于4的人)+1+2+3+2+1>4*5 所以可行換個情況 現在的工資總和為35 所以我們分兩種情況看①6個人,扣5次==>和為30那么一共有四個人可以扣5次這四個人的工資為(5 7 8 6)==>和為20而剩下的人工資為(1 2 3 2 1)==>和為94*5+1+2+3+2+1<5*6這種情況不行②4個人,扣8次==>和為32那么可以扣8次的人只有一個而其余的人,工資就要全部被扣光所以8*1+1+2+5+7+6+3+2+1>32所以這是可以的為什么下面這個扣款之和比上面的大,為什么下面的可以上面的卻不可以呢?? 請結合上面的分析進行考慮。解法
這道題目有兩種解法,一種是線段樹,一種是樹狀數組
先說線段樹
這道題目一開始我是用線段樹做的
線段樹就很簡單了,基本上就是一個模板題
上面的過程其實就是一個簡單的求和,唯一需要注意的是工資大于y的人在求和時只能+y
但是由于數據太過龐大,所以怎么求都要超時,這種做法我們不提倡
樹狀數組
那么樹狀數組我們就不能簡單地學習上面的做法了
因為如上述做法所述
對于工資大于y的人,我們只能夠在求和時+y
所以樹狀數組的簡單求和顯然是不行的
換個思路考慮,我們如果有辦法快速地知道有多少人的工資是大于y的,有多少人的工資是小于y的,并且能夠快速求小于y的人的工資之和,那么這樣也解決了相同的問題。
下面我們就來依次解決上述問題
①快速求人數
首先想到的肯定是記錄某個工資對應的人數
但是由于工資的范圍在[0,10^9],所以這樣肯定是要爆空間
解決辦法
我們可以記錄每個人工資的大小位置,然后對位置進行疊加
舉例
那么在查詢有多少人工資比他低的人就只需要查詢它工資的位置就好
例如
于是,對于扣除的工資數y,我們只要求出工資比y低的人數,用 總人數-工資比y低的人數=工資不低于y的人數
②快速求工資小于y的人的工資總和
有了上述解法,下面的問題就好解決了
解法
再開一個數組,對于每次修改,在對應位置上增加工資即可
舉例
所以求工資小于y的人的工資總和,只要簡單的在這個數組當中求和即可
上述例子中 工資 1 2 7 8 9 5 9位置 1 2 3 4 5 6 7 標記 1 2 5 7 8 18 (因為9的位置都是6,所以在6上記錄為9+9=18) 查詢工資比7低的人的工資之和=>位置4之前的工資之和=1+2+5=8問題解決
附上代碼
#include <cstdio> #include <iostream> #include <algorithm> #define lowbit(i) i&-i using namespace std;char mov[201234]; long long n,m,cnt[201234],sum[201234],to[201234],add[201234],loc[201234]={0,0},data[201234];inline int input() {char c=getchar();int o;while(c>57||c<48)c=getchar();for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;return o; }void ADD(int Loc,int C,int S){for(int i=Loc;i<=m;i+=lowbit(i))cnt[i]+=C,sum[i]+=S;} long long count(int Loc) {long long ans=0;for(int x=Loc;x>0;x-=lowbit(x))ans+=cnt[x];return ans; } long long SUM(int Loc) {long long ans=0;for(int i=Loc;i;i-=lowbit(i))ans+=sum[i];return ans; }int main() {//freopen("In.txt","r",stdin);//freopen("True.txt","w",stdout);scanf("%d%d\n",&n,&m);for(int i=1;i<=m;i++){mov[i]=getchar();to[i]=input();add[i]=input();loc[i]=add[i];}sort(loc+1,loc+m+2);ADD(1,n,0);for(int i=1;i<=m;i++){if(mov[i]=='U'){ADD((lower_bound(loc+1,loc+m+2,data[to[i]])-loc),-1,-data[to[i]]);ADD((lower_bound(loc+1,loc+m+2,add[i])-loc),1,add[i]);data[to[i]]=add[i];}else{int Loc=(lower_bound(loc+1,loc+m+1,add[i])-loc)-1,t=count(Loc),s=SUM(Loc);if(1LL*(n-count(Loc))*add[i]+SUM(Loc)>=1LL*add[i]*to[i])puts("TAK");else puts("NIE");}}return 0; }順便附上對拍代碼
#include<cstdlib> #include<cstdio> #include<ctime> #include<iostream> #include<cstring>int A[123456]={987654321};using namespace std; int main() {freopen("In.txt","w",stdout);srand(time(NULL));int n=rand()%100000+1,m=rand()%100000+1;printf("%d %d\n",n,m);for(int i=1;i<=m;i++){int c=rand()%2,a=rand()%n+1,add=rand()%1000000000;printf("%c %d %d\n",(c?'U':'Z'),a,add);}return 0; }對拍文件
運行對拍代碼可以得到In.txt(輸入數據)
運行刪去//后的我的代碼可以得到True.txt(正解)
總結
以上是生活随笔為你收集整理的NKOJ-3776 工资管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: signature=bf81fe7f4f
- 下一篇: java 5分钟_java-时间格式化