日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NKOJ-3776 工资管理

發布時間:2023/12/20 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NKOJ-3776 工资管理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

P3776工資管理
時間限制 : - MS 空間限制 : 165536 KB
評測說明 : 1000ms
問題描述

何老板的公司有n名員工,編號1到n。一開始所有員工的工資都是0。根據何老板的心情好壞,可能出現下列兩種針對員工工資的操作: 1.U x y 改工資操作:何老板將第x號員工的工資改成了y; 2.Z x y 減工資操作:何老板生氣了,他想選出x個員工,并將他們的工資全都減去1。何老板想知道,他能否一口氣進行y次這樣的減工資操作。能輸出TAK,否則輸出NIE。注意,員工的工資不能為負。對于每個減工資的操作,何老板只是在心里想想,口頭上說說,嚇唬嚇唬大家,解解悶氣,他并不會真正執行。即不會對任何人的工資進行修改。

輸入格式

第一行包含兩個正整數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所以這是可以的為什么下面這個扣款之和比上面的大,為什么下面的可以上面的卻不可以呢?? 請結合上面的分析進行考慮。

解法

這道題目有兩種解法,一種是線段樹,一種是樹狀數組

先說線段樹

這道題目一開始我是用線段樹做的
線段樹就很簡單了,基本上就是一個模板題

先把線段樹畫出來,狀態定義就是 對于線段[l,r],表示的就是l號員工到r號員工的狀態 我們記錄[l,r]的工資最大值和最小值那么對于扣款次數y,如果[l,r]的最小值大于等于y,說明[l,r]內所有的人的工資不少于y 所以這個區間可以被扣(r-l+1)*y塊錢而對于[l,r]的最小值小于y,且最大值也小于y,就說明[l,r]內所有的人都不能夠被扣y次 換句話說,這些人的錢要全部被扣完 所以對這個區間我們選擇求和對于[l,r]的最小值小于y,最大值大于y 我們選擇進行進一步的細分討論(再把區間二分)

上面的過程其實就是一個簡單的求和,唯一需要注意的是工資大于y的人在求和時只能+y

但是由于數據太過龐大,所以怎么求都要超時,這種做法我們不提倡

樹狀數組

那么樹狀數組我們就不能簡單地學習上面的做法了
因為如上述做法所述
對于工資大于y的人,我們只能夠在求和時+y
所以樹狀數組的簡單求和顯然是不行的

換個思路考慮,我們如果有辦法快速地知道有多少人的工資是大于y的,有多少人的工資是小于y的,并且能夠快速求小于y的人的工資之和,那么這樣也解決了相同的問題。

下面我們就來依次解決上述問題

①快速求人數
首先想到的肯定是記錄某個工資對應的人數
但是由于工資的范圍在[0,10^9],所以這樣肯定是要爆空間
解決辦法
我們可以記錄每個人工資的大小位置,然后對位置進行疊加
舉例

假設每個人的工資為1 2 7 8 5 9 那么工資的大小排序1 2 4 5 3 6那么在修改時我們對工資為1的點,我們就在位置1上加1工資為2的點,我們就在位置2上加1工資為7的點,我們就在位置4上加1 簡言之,在修改時,對工資修改是在它的大小位置上添加標記(別忘了在原位置上減1)

那么在查詢有多少人工資比他低的人就只需要查詢它工資的位置就好
例如

上述例子中 工資 1 2 7 8 9 5 9 查詢工資比5低的人,5的位置是3,所以查詢1 2位置上的標記個數即可

于是,對于扣除的工資數y,我們只要求出工資比y低的人數,用 總人數-工資比y低的人數=工資不低于y的人數

②快速求工資小于y的人的工資總和
有了上述解法,下面的問題就好解決了
解法
再開一個數組,對于每次修改,在對應位置上增加工資即可
舉例

假設每個人的工資為1 2 7 8 5 9 那么工資的大小排序1 2 4 5 3 6那么在修改時我們對工資為1的點,我們就在位置1上加1工資為2的點,我們就在位置2上加2工資為7的點,我們就在位置4上加7 簡言之,在修改時,對工資修改是在它的大小位置上修改工資(別忘了在原位置上減去原先工資)

所以求工資小于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 工资管理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。