必须吐槽一下……这场比赛真是原题大战……我一开始只看出来几道题是SCU上的……后来有人我告诉我是2015四川赛区的一整套题……出题人是厉害……
A.Easy Math
比赛的时候做一个大胆地猜想……只有每个数开方后都是整数它们的和才都是整数。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<ctime> #define LL long long #define db double #define EPS 1e-1 #define inf 1e16 #define pa pair<int,int> using namespace std; int main(){ int n,x; while (scanf("%d",&n)!=EOF){ for (int i=1;i<=n;i++) scanf("%d",&x); bool flag=1; if (sqrt(x)-floor(sqrt(x))>0.0) { flag=0; } if (flag) puts("Yes"); else puts("No"); } return 0; }
注意到0≤ai≤10^9,这样只需要枚举在第10^k有多少个数产生进位就可以了。
注意到答案和顺序无关,只需要保证一对数不能计重就行,也就是说排序不影响结果。
那么枚举k,令s=10^k 。
每次对数组都mod s ,然后排序。
题目转化为:现在已知a< s, b < s,现在已知a,求有多少个b使得a+b>=s。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<ctime> #define LL long long #define db double #define EPS 1e-1 #define inf 1e16 using namespace std; const int MAXN=1e+5+1000; int n; LL A[MAXN],B[MAXN]; int main(){ while (~scanf("%d",&n)){ for (int i=0;i<n;i++) scanf("%lld",&A[i]); sort(A,A+n); LL cnt=0,s=10; for (int k=1;k<=10;k++,s*=10){ for (int i=0;i<n;i++) B[i]=A[i]%s; sort(B,B+n); for (int i=0;i<n;i++){ LL t=s-B[i]; if (B[n-1]>=t){ int pos=lower_bound(B,B+n,t)-B; if (pos>i) cnt+=n-pos; else cnt+=n-(i+1); } } } printf("%lld\n",cnt); } return 0; }
C.Censor
套用KMP模板求得失配数组,再开一个栈保存没有被删除的字符以及已经匹配了a字符串的字符个数。如果在b中完整匹配到了a,则把length(a)个字符出栈,然后取栈顶保存的已匹配的字符个数继续去匹配a,栈为空则从0开始。复杂度O(n)。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<ctime> #define LL long long #define db double #define EPS 1e-1 #define inf 1e16 using namespace std; const int N = 5e6+5; struct PP { char c; int cnt; }sta[N]; int f[N]; char w[N], p[N], res[N]; int wlen, plen; int hehe(){ int cur=0,cnt=0; for (int i=0;i<plen;i++){ while (cnt && w[cnt]!=p[i]) cnt=f[cnt]; if (w[cnt]==p[i]){ sta[cur++]=(PP){p[i],cnt+1}; cnt++; } else { sta[cur++]=(PP){p[i],0}; cnt=0; } if (cnt==wlen){ cur-=wlen; if (cur==0) cnt=0; else cnt=sta[cur-1].cnt; } } return cur; } void getFail(char *p, int *f, int m) { f[0]=0, f[1]=0; for(int i = 1;i < m; i++) { int j = f[i]; while(j && p[i] != p[j]) j = f[j]; f[i+1] = (p[i] == p[j])?j+1:0; } } void solve(){ wlen=strlen(w),plen=strlen(p); getfail(w,f,wlen); int num=hehe(); for (int i=0;i<num;i++) putchar(sta[i].c); puts(""); } int main(){ while(scanf("%s%s",w,p)!=EOF) solve(); return 0; }
只搜索前30个点的选与不选,如果还有边没有被覆盖则一定选非前30的点来覆盖。这样一定可以搜索到最优解。
如果一个点不选则起相邻的点必选。
所以我们初始化所有的点都选,然后搜索哪些点不选。
当我们搜索到一个点的时候,判断它相邻的点是否有不选的,若有则必选;否则我们可以选择不选该点。这样搜索出来的状态对于前30个点之间的边来说一定是合法的。再加上最优性剪枝就可过这题。
搜索的做法:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<ctime> #define LL long long #define db double #define EPS 1e-1 #define inf 1e16 using namespace std; struct node{ int to,next; }E[550*550*2]; pair<int,int>a[35]; bool use[35],tu[35][35]; int N,ans,du[550],flag,fuck[550]; int head[550],num; int n,m; void prepare(){ memset(head,-1,sizeof(head)); num=0; } void add(int u,int v){ E[num].to=v; E[num].next=head[u]; head[u]=num++; E[num].to=u; E[num].next=head[v]; head[v]=num++; } void dfs(int id,int val){ if (val+flag>ans) return ; if (id==N+1){ ans=min(ans,val+flag); return ; } bool ok=1; int w; for (int i=1;i<=N;i++){ if (tu[id][i] && use[i]) ok=0; } if (ok){ use[id]=1; for (int i=head[id];i+1;i=E[i].next){ w=E[i].to; if (fuck[w]==0) flag++; fuck[w]++; } dfs(id+1,val); for (int i=head[id];i+1;i=E[i].next){ w=E[i].to; fuck[w]--; if (fuck[w]==0) flag--; } use[id]=0; } dfs(id+1,val+1); } int main(){ int i,u,v; while (scanf("%d%d",&n,&m)!=EOF){ prepare(); memset(tu,false,sizeof(tu)); memset(fuck,0,sizeof(fuck)); memset(du,0,sizeof(du)); for (int i=1;i<=m;i++){ scanf("%d%d",&u,&v); if (u>30 || v>30) add(u,v); du[u]++, du[v]++; if (u<=30 || v<=30) tu[u][v]=tu[v][u]=1; } flag=0; N=min(30,n); memset(use,false,sizeof(use)); ans=m; dfs(1,0); printf("%d\n",ans); } return 0; }
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<ctime> #define LL long long #define db double #define EPS 1e-1 #define inf 1e16 using namespace std; int n,m; int link[505],mp[505][505]; bool used[505]; bool dfs(int u){ int v; for (v=1;v<=n;v++) if (mp[u][v] && !used[v]){ used[v]=1; if (link[v]==-1 || dfs(link[v])){ link[v]=u; return true; } } return false; } int hungary(){ int res=0; int u; memset(link,-1,sizeof(link)); for (u=1;u<=n;u++){ memset(used,0,sizeof(used)); if (dfs(u)) {res++;} } return res; } int main(){ while(scanf("%d%d",&n,&m)!=EOF){ memset(mp,0,sizeof(mp)); for (int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); mp[u][v]=1; mp[v][u]=1; } printf("%d\n",hungary()/2); } }
一道思考题,推推结论,找找规律就好了。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<ctime> #define LL long long #define db double #define EPS 1e-1 #define inf 1e16 using namespace std; LL n,m,k; int main() { while(~scanf("%lld%lld%lld",&n,&m,&k)){ LL ans=0; for(LL x=1;x<=n;x++){ LL tmax=(k-2*x)/2; if(tmax>0){ if(tmax>=m)tmax=m; if(tmax%2==0){ ans+=(n-x+1)*(m+m-tmax+1)*(tmax/2); } else { ans+=(n-x+1)*((m+m-tmax+1)/2)*tmax; } } } printf("%lld\n",ans); } return 0; }
枚举值为10000的数。确定值为10000的数的位置后,环就断成了一条链。我们可以用动态规划求出(1,i)的数的不增序列的最大和,同理求出(i,n)的不增序列的最大和。然后枚举断点即可。在动态规划的过程中需要用到树状数组优化转移(线段树会T)。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<ctime> #define LL long long #define db double #define EPS 1e-1 #define inf 1e16 const int nn = 110000; int n; int a[nn]; int c[nn*2]; int f1[nn],f2[nn]; int tree[11000]; inline int lowbit(int x) { return x&(-x); } int getmax(int id) { int re=0; while(id>0) { re=max(re,tree[id]); id-=lowbit(id); } return re; } void add(int id,int val) { if(id==0) return ; for(int i=id;i<=10000;i+=lowbit(i)) { tree[i]=max(tree[i],val); } } int solve(int id) { int i; f1[0]=0; for(i=1;i<=10000;i++) tree[i]=0; int tem; for(i=id+1;i<id+n;i++) { if(c[i]==10000) { f1[i-id]=f1[i-id-1]; continue; } tem=getmax(10000-c[i]+1)+c[i]; f1[i-id]=max(f1[i-id-1],tem); add(10000-c[i]+1,tem); } f2[n]=0; for(i=1;i<=10000;i++) tree[i]=0; for(i=id+n-1;i>=id+1;i--) { if(c[i]==10000) { f2[i-id]=f2[i-id+1]; continue; } tem=getmax(10000-c[i]+1)+c[i]; f2[i-id]=max(f2[i-id+1],tem); add(10000-c[i]+1,tem); } int re=0; for(i=0;i<=n-1;i++) re=max(re,f1[i]+f2[i+1]); return re; } int ve[20],lv; int main() { int i; while(scanf("%d",&n)!=EOF) { lv=0; for(i=0;i<n;i++) { scanf("%d",&a[i]); c[i]=a[i]; c[i+n]=a[i]; if(a[i]==10000) ve[lv++]=i; } int ans=0; for(i=0;i<lv;i++) { ans=max(ans,solve(ve[i])); } printf("%d\n",ans+10000); } return 0; }
神建图的一道题
H.Range Query
完备匹配的最小字典序。 先找出最大匹配,然后枚举从小到每个点枚举最优状态,删边然后继续寻找增广路。
我抄的这道题代码……因为带字典序我就不会做了……毕竟太弱了(结果居然拿了个FB)。
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> using namespace std; const int mmax = 60; int max_num[mmax],min_num[mmax]; int L[mmax],R[mmax]; bool G[mmax][mmax]; int n,m1,m2; void init() { memset(G,0,sizeof G); for(int i=1;i<=n;i++) { L[i]=min_num[i]=1; R[i]=max_num[i]=n; } } int link[mmax]; bool vis[mmax]; int Match[mmax]; bool match(int x) { for(int i=1;i<=n;i++) { if(G[x][i] && !vis[i]) { vis[i]=1; if(link[i]==-1 || match(link[i])) { link[i]=x; Match[x]=i; return 1; } } } return 0; } int hungury() { int cnt=0; memset(link,-1,sizeof link); for(int i=1;i<=n;i++) { memset(vis,0,sizeof vis); if(match(i)) cnt++; } return cnt; } int main() { int a,b,c; while(~scanf("%d %d %d",&n,&m1,&m2)) { init(); for(int i=1;i<=m1;i++) { scanf("%d %d %d",&a,&b,&c); for(int j=a;j<=b;j++) min_num[j]=max(min_num[j],c); L[c]=max(L[c],a); R[c]=min(R[c],b); } for(int i=1;i<=m2;i++) { scanf("%d %d %d",&a,&b,&c); for(int j=a;j<=b;j++) max_num[j]=min(max_num[j],c); L[c]=max(L[c],a); R[c]=min(R[c],b); } for(int i=1;i<=n;i++) { for(int j=min_num[i];j<=max_num[i];j++) { if(L[j]<=i && i<=R[j]) G[i][j]=1; } } int num = hungury(); if(num==n) { // for(int i=1;i<=n;i++) // printf("%d%c",Match[i],i==n?'\n':' '); for(int i=1;i<=n;i++) { int tmp=Match[i]; G[i][tmp]=0; link[tmp]=-1; bool fg=0; memset(vis,0,sizeof vis); for(int j=1;j<tmp;j++) { if(!vis[j] && G[i][j]) { vis[j]=1; if(link[j]==-1 || match(link[j])) { link[j]=i; Match[i]=j; fg=1; break; } } } if(!fg) { G[i][tmp]=1; link[tmp]=i; } tmp=Match[i]; for(int j=1;j<=n;j++) G[j][tmp]=0; } for(int i=1;i<=n;i++) printf("%d%c",Match[i],i==n?'\n':' '); } else puts("-1"); } return 0; }
不会,不想补了,POI2007原题。
J.Right turn
模拟题。
对于出现过的每一个x建立一个set 集合,对于y也如此。然后查找要走到哪个点即可,主要要对状态记录,判断是否无限循环。
#include <set> #include <map> #include <cmath> #include <stack> #include <queue> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long int LL; const int M = 1009,INF = 0x3fffffff; vector<pair<int, vector<int> > > vx, vy; struct place {int x, y ,dir;}s; vector<struct place> repeat; bool operator == (struct place a, struct place b) { return a.x == b.x && a.y == b.y && a.dir == b.dir; } void input(int n) { for (int i = 0; i < n; i++) { int x, y; scanf("%d%d", &x, &y); bool key = true; for (int j = 0; j < vx.size(); j++) { if (vx[j].first == x) { vx[j].second.push_back(y); key = false; break; } } if (key) { pair<int, vector<int> >p; p.first = x; p.second.push_back(y); vx.push_back(p); } key = true; for (int j = 0; j < vy.size(); j++) { if (vy[j].first == y) { vy[j].second.push_back(x); key = false; break; } } if (key) { pair<int, vector<int> >p; p.first = y; p.second.push_back(x); vy.push_back(p); } } } bool move(void) { if (s.dir == 0) { for (int i = 0; i < vy.size(); i++) { if (vy[i].first == s.y) { int temp = INF; bool ok = false; for (int j = 0; j < vy[i].second.size(); j++) { if(vy[i].second.at(j) > s.x && vy[i].second.at(j) < temp) { temp = vy[i].second.at(j); ok = true; } } s.x = temp - 1; s.dir = 1; return ok; } } } if (s.dir == 1) { for (int i = 0; i < vx.size(); i++) { if (vx[i].first == s.x) { int temp = -INF; bool ok = false; for (int j = 0; j < vx[i].second.size(); j++) { if(vx[i].second.at(j) < s.y && vx[i].second.at(j) > temp) { temp = vx[i].second.at(j); ok = true; } } s.y = temp + 1; s.dir = 2; return ok; } } } if (s.dir == 2) { for (int i = 0; i < vy.size(); i++) { if (vy[i].first == s.y) { int temp = -INF; bool ok = false; for (int j = 0; j < vy[i].second.size(); j++) { if(vy[i].second.at(j) < s.x && vy[i].second.at(j) > temp) { temp = vy[i].second.at(j); ok = true; } } s.x = temp + 1; s.dir = 3; return ok; } } } if (s.dir == 3) { for (int i = 0; i < vx.size(); i++) { if (vx[i].first == s.x) { int temp = INF; bool ok = false; for (int j = 0; j < vx[i].second.size(); j++) { if(vx[i].second.at(j) > s.y && vx[i].second.at(j) < temp) { temp = vx[i].second.at(j); ok = true; } } s.y = temp - 1; s.dir = 0; return ok; } } } return false; } bool judge_infinite(void) { for (int i = 0; i < repeat.size(); i++) { if (s == repeat[i]) return true; } return false; } int main(void) { //problem: soj4445, address:http://acm.scu.edu.cn/soj/problem.action?id=4445 int n; while (~scanf("%d", &n)) { vx.clear(); vy.clear(); repeat.clear(); input(n); s.x = s.y = s.dir = 0; for (int i = 0;; i++) { repeat.push_back(s); if (!move()) { printf("%d\n", i); break; } if (judge_infinite()) { printf("-1\n"); break; } } } return 0; }