国庆清北刷题冲刺班 Day2 上午
2017-10-09 15:56
260 查看
一道图论神题(god)
Time Limit:1000ms Memory Limit:128MB题目描述
LYK有一张无向图G={V,E},这张无向图有n个点m条边组成。并且这是一张带权图,只有点权。
LYK想把这个图删干净,它的方法是这样的。每次选择一个点,将它删掉,但删这个点是需要代价的。假设与这个点相连的还没被删掉的点是u1,u2,…,uk。LYK将会增加a[u1],a[u2],…,a[uk]的疲劳值。
它想将所有点都删掉,并且删完后自己的疲劳值之和最小。你能帮帮它吗?
输入格式(god.in)
第一行两个数n,m表示一张n个点m条边的图。
第二行n个数ai表示点权。
接下来m行每行三个数u,v,表示有一条连接u,v的边。数据保证任意两个点之间最多一条边相连,并且不存在自环。
输出格式(god.out)
你需要输出这个最小疲劳值是多少。
输入样例
4 3
10 20 30 40
1 4
1 2
2 3
输出样例
40
样例解释
一个合理的方法是先删4号点,此时有10点疲劳值。接下来删3号点,获得20点疲劳值,再删2号点,获得10点疲劳值,最后删1号点,没有疲劳值。总计40点疲劳值。
对于30%的数据n<=10。
对于60%的数据n,m<=1000。
对于100%的数据1<=n,m,ai<=100000
有限删除点权大的点,虽然我无法证明他的正确性,考场上没指望这样能得分,写了个int,潦草退场,结果是正解,开long long A掉。。。
这里写代码片
位运算2(bit)
Time Limit:1000ms Memory Limit:128MB题目描述
LYK拥有一个十进制的数N。它赋予了N一个新的意义:不考虑N的符号,将N每一位都拆开来后再加起来就是N所拥有的价值。例如数字123拥有6的价值,数字999拥有27的价值,数字-233拥有8的价值。
假设数字N的价值是K,LYK想找到一个价值是K+1的数字,当然这个答案实在太多了,LYK想使得这个价值为K+1的数字尽可能大,并且需要保证这个数字小于N。
输入格式(bit.in)
一个整数N。
输出格式(bit.out)
一个数表示答案。你需要输出一个整数,且这个数不包含前导0。
输入样例1
199
输出样例1
-299
输入样例2
1520
输出样例2
1512
对于20%的数据|N|<=10
对于40%的数据|N|<=100
对于60%的数据|N|<=10^9
对于80%的数据|N|<=10^1000
对于100%的数据|N|<=10^100000。
分析:其实就是一道比较恶心的讨论题.如果|N| <= 100000(甚至可以更大),可以暴搜。如果N是负数,从后往前第一位不是9的数上+1,如果N不是负数,那么肯定存在一个第i位,第i位上的数-1,第i位后面的数分摊+2,再把后面的数重新组合一下,把尽量多的数分摊在前面的位上。如果不存在这么一个第i位,那么最后输出的肯定是一个负数,第一位++,添上负号就可以了.如果第一位是9,就在前面补上1。
但是全部乱搞,不知怎么的WA了20分。
#include <cmath> #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> #include <string> #include <cstring> using namespace std; char s[100005]; int len,sum,FLAG; int main() { freopen("bit.in","r",stdin); freopen("bit.out","w",stdout); scanf("%s",s); int i,j; FLAG=0; if (s[0]!='-') { len=strlen(s); sum=0; for (i=len-1; i>=0; i--) { if (s[i]>='1' && sum>=2) { s[i]=char(s[i]-1); sum=2; for (j=i+1; j<len; j++) while (sum && s[j]<'9') --sum,s[j]=char(s[j]+1); int SS=0; for (j=i+1; j<len; j++) SS+=s[j]-'0'; for (j=i+1; j<len; j++) { if (SS>=9) s[j]='9',SS-=9; else s[j]=char(SS+'0'),SS=0; } FLAG=1; break; } sum+='9'-s[i]; } if (FLAG) { for (i=0; i<len; i++) if (s[i]!='0') break; for (j=min(len-1,i); j<len; j++) printf("%c",s[j]); printf("\n"); return 0; } printf("-"); for (i=len-1; i>=0; i--) if (s[i]<'9') { s[i]=char(s[i]+1); FLAG=1; break; } if (FLAG) { for (i=0; i<len; i++) if (s[i]!='0') break; for (j=min(len-1,i); j<len; j++) printf("%c",s[j]); printf("\n"); return 0; } printf("1"); for (i=0; i<len; i++) if (s[i]!='0') break; for (j=min(len-1,i); j<len; j++) printf("%c",s[j]); printf("\n"); return 0; } FLAG=0,len=strlen(s); printf("-"); for (i=len-1; i>=1; i--) if (s[i]<'9') { s[i]=char(s[i]+1); FLAG=1; break; } if (FLAG) { for (i=1; i<len; i++) if (s[i]!='0') break; for (j=min(len-1,i); j<len; j++) printf("%c",s[j]); printf("\n"); return 0; } printf("1"); for (i=1; i<len; i++) if (s[i]!='0') break; for (j=min(len-1,i); j<len; j++) printf("%c",s[j]); printf("\n"); return 0; }
逆序对(pair)
Time Limit:1000ms Memory Limit:128MB题目描述
LYK最近在研究逆序对。
这个问题是这样的。
一开始LYK有一个2^n长度的数组ai。
LYK有Q次操作,每次操作都有一个参数k。表示每连续2^k长度作为一个小组。假设n=4,k=2,则a[1],a[2],a[3],a[4]为一个小组,a[5],a[6],a[7],a[8]为一个小组,a[9],a[10],a[11],a[12]为一个小组,a[13],a[14],a[15],a[16]也为一个小组。
然后LYK对于每个小组都翻转,也就是说原数组会变成a[4],a[3],a[2],a[1],a[8],a[7],a[6],a[5],a[12],a[11],a[10],a[9],a[16],a[15],a[14],a[13]。之后它想求出这2^n个数的逆序对是多少。
因此你需要输出对于每次操作,操作完后这2^n个数的逆序对有多少对。
两个数ai,aj被称为逆序对当且仅当i
#include<iostream> #include<cstring> #include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; typedef long long LL; #define MAXN 17 const int MAXM = 1<<17; int n,tot; int a[MAXM+2],res[MAXM+2],bit[MAXM+2],P[MAXM+2]; LL st[MAXM+2][MAXN+1],ST[MAXM+2][MAXN+1]; LL rev[MAXN+1],pos[MAXN+1],same[MAXN+1],sum[MAXN+1]; inline void read(int &x){ x=0; int f=1; char c=getchar(); while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); } while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f; } void Xsort(int l,int r){ if(l == r) return ; int Mid = l+r >> 1; Xsort(l,Mid),Xsort(Mid+1,r); for(int p=r; p>=l; --p) P[p]=p; for(int p=r-1;p>=l;--p) if(a[p] == a[p+1]) P[p]=P[p+1]; int i=l,j=Mid+1,k=l; while(i<=Mid && j<=r ){ if(a[i] > a[j]) st[l][bit[r-l+1]] += Mid-i+1, res[k++] = a[j++]; else if(a[i] == a[j]) ST[l][bit[r-l+1]] += P[j]-j+1,res[k++] = a[i++]; else res[k++] = a[i++]; } while(i <= Mid) res[k++] = a[i++]; while(j <= r) res[k++] = a[j++]; for(k=l; k<=r; ++k) a[k]=res[k]; } void Pre(){ for(int i=1; i<=n; ++i) sum[i] = 1LL * (1<<i - 1) * (1<<i - 1); for(int i=1,len=2; i<=n; ++i,len<<=1) for(int j=1; j<=tot; j+=len) rev[i] += st[j][i],pos[i] += sum[i] - st[j][i],same[i] += ST[j][i]; } int main(int argc,char *argv[]){ freopen("pair.in","r",stdin); freopen("pair.out","w",stdout); read(n); tot = 1<<n; for(int i=2; i<=tot; ++i) bit[i] = bit[i>>1] + 1; for(int i=1; i<=tot; ++i) read(a[i]); Xsort(1,tot); Pre(); int m,k; read(m); LL Ans=0; while(m--){ read(k); Ans=0; for(int i=1; i<=k; ++i) swap(rev[i],pos[i]),rev[i]-=same[i],pos[i] = sum[i] * tot / (1<<i) - rev[i]; for(int i=1; i<=n; ++i) Ans += rev[i]; printf("%I64d\n",Ans); } fclose(stdin);fclose(stdout); return 0; }
以下分析来自cnblog TRTTG
例:1 2 3 4 5 6 7 8 k=3
翻转结果为 8 7 6 5 4 3 2 1
将翻转过程拆分:
第一步: 2 1 4 3 6 5 8 7
第二步:4 3 2 1 8 7 6 5
第三步:8 7 6 5 4 3 2 1
所以每一次翻转都可以拆分,而拆分的过程就是交换相邻的2^(步数-1)
所以归并排序求逆序对的时候,用st表维护从第i个位置,长为2^j的区间的逆序对个数
然后求出对应区间的顺序对个数
维护所有长为2^i的区间的逆序对个数rev[i]和顺序对个数pos[i]
统计答案的时候,拆分就是swap(rev,pos)
在这里顺序对个数是用总个数-逆序对个数求的
所以就会有一个问题,例:
2 2 3 3
实际上的:
pos : 0 0
rev : 0 0
但求的时候,pos :2 4
因为用总个数-逆序对个数=顺序对个数+相等的数对
所以归并过程中,还要预处理从第i个位置,长为2^j的区间的相等数对个数
计算rev,pos时,同时计算same[i],表示所有长为2^i的区间相等的数对个数
求解的时候 先swap(rev,pos),然后rev-=same,再 pos=总的-rev
相关文章推荐
- 国庆清北刷题冲刺班 Day2 下午
- 国庆清北刷题冲刺班 Day5 上午
- 济南刷题冲刺 Day2 上午
- 国庆清北刷题冲刺班 Day6 上午
- 国庆清北刷题冲刺班 Day7 上午
- 国庆清北刷题冲刺班 Day4 上午
- 国庆七天乐 Day2
- 【每日scrum】第一次冲刺day2
- 2017国庆郑州集训Day2
- Beta版本敏捷冲刺每日报告——Day2
- Beta冲刺---Day2
- Beta冲刺Day2
- 【Alpha版本】冲刺-Day2
- 2017国庆郑州集训Day2
- Alpha冲刺——Day2
- 十天冲刺---Day2
- Beta冲刺 day2
- Beta版本冲刺——day2
- 2017 国庆湖南Day2
- Apha冲刺! Day2 - 砍柴