AB水题,C是斜率优化的动态规划。
报告从D开始。
D 高精度乘法。
题目的bug带来了比较大的困扰,之后许多弱弱的小毛病也耽误了不少时间
#include <cstdio> #include <cstdlib> #include <cstring> #define maxn 10010 int main(){ char a[maxn],b[maxn]; int ans[maxn], t1[maxn], t2[maxn]; int len1, len2, len, positive1, positive2; while (scanf("%s%s", a, b)==2){ memset(t1, 0, sizeof(int)*maxn); memset(t2, 0, sizeof(int)*maxn); memset(ans, 0, sizeof(int)*maxn); //a for (int i=0;; i++) if (a[i]=='/0'){len1=i-1; break;} if (a[0]=='-'){positive1=false;len1--;} else{positive1=true;} for (int i=((positive1)?len1:len1+1), j=0; i>=0; i--, j++) t1[j]=a[i]-'0'; //b for (int i=0;; i++) if (b[i]=='/0'){len2=i-1; break;} if (b[0]=='-'){positive2=false;len2--;} else{positive2=true;} for (int i=((positive2)?len2:len2+1), j=0; i>=0; i--, j++) t2[j]=b[i]-'0'; //len len=(len1>len2)?len1:len2; if (positive1==positive2){ //a+b or (-a)+(-b) a>0 b>0 for (int i=len; i>=0; i--){ ans[i]=t1[i]+t2[i]; int j=i; if (ans[j]>=10){ ans[j+1]+=ans[j]/10; ans[j]%=10; j++; } if (j>len) len=j; } while (ans[len]==0 && len>=0) len--; if (ans[len]==0){ printf("0/n"); } else{ if (!positive1) printf("-"); for (int i=len; i>=0; i--) printf("%d", ans[i]); printf("/n"); } } else{ //if abs(t1) < abs(t2) exchange bool exchange=false; if (len1<len2) exchange=true; else if (len1==len2){ for (int i=len1; i>=0; i--) if (t1[i]<t2[i]){exchange=true;break;} } if (exchange){ int t; for (int i=0; i<=len; i++){ t=t1[i]; t1[i]=t2[i]; t2[i]=t; } t=len1; len1=len2; len2=t; t=positive1; positive1=positive2; positive2=t; } //t1-t2 abs(t1)>abs(t2) for (int i=0; i<=len; i++) ans[i]=(i<=len1?t1[i]:0)-(i<=len2?t2[i]:0); for (int i=0; i<=len; i++) if (ans[i]<0) {ans[i]+=10; ans[i+1]--;} if (a[len+1]==-1){ if (positive2) positive1=true; else positive1=false; }else{ if (positive2) positive1=false; else positive1=true; } while (ans[len]<=0 && len>=0) len--; if (ans[len]==0){ printf("0/n"); } else{ if (positive2) printf("-"); for (int i=len; i>=0; i--) printf("%d", ans[i]); printf("/n"); } } } return 0; }
E 找每点最近三个点
赛后写的算法其实和比赛时思路是一致的。这个算法肯定不算是正统的, 不过解决一些随机产生的,分布比较平均的数据应该还是可以的(含有人品因素)。具体解法是:把所有点进行一个先比x再比y的排序,然后对每个点i在排序表里上/下交替地找一些点j进行比较,当i和j的x坐标差大于得到的最近三个距离的时候,就可以停止枚举了。
写的时候没有交替地在排序表里上/下寻找邻进点,时间花得比较多。
还有在精度问题上卡了非常久,最后还是放宽了很多地方才AC的。
#include <cstdio> #include <cstdlib> #include <cmath> #include <algorithm> using namespace std; #define maxn 20010 #define MAXINT (1<<30) const int EPS=1E-6; struct node{ int order; int id; double x,y; }q[maxn]; int cmp(const void *a, const void *b){ struct node *aa=(node *)a; struct node *bb=(node *)b; return ((aa->x - bb->x)>-EPS?1:-1); } double dis(struct node a, const node b){ return ((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int main(){ freopen("10k.in", "r", stdin); freopen("ans.txt","w",stdout); int ansid[maxn][3]; double ans[maxn][3]; int orid[maxn]; //origin id int n=0; while (scanf("%d%lf%lf", &q[n].id, &q[n].x, &q[n].y)==3){ q[n].order=n; orid[n]=q[n].id; n++; } qsort(q, n, sizeof(q[0]), cmp); for (int i=0; i<n; i++){ int order=q[i].order; for (int j=0; j<3; j++) ans[order][j]=MAXINT; int maxpos=1; //ans[i]中最大的是ans[or][maxpos] for (int j=i-1; j>=0; j--){ double t; if (q[i].x-q[j].x-ans[order][maxpos]>1)break; if (ans[order][maxpos]>(t=dis(q[i], q[j]))){ ans[order][maxpos]=t; ansid[order][maxpos]=q[j].order; for (int k=0; k<3; k++) //ans[or][k] > ans[or][maxpos] if (ans[order][k]>ans[order][maxpos]) maxpos=k; } } for (int j=i+1; j<n; j++){ double t; if (q[j].x-q[i].x-ans[order][maxpos]>1)break; if (ans[order][maxpos]>(t=dis(q[i], q[j]))){ ans[order][maxpos]=t; ansid[order][maxpos]=q[j].order; for (int k=0; k<3; k++) //ans[or][k] > ans[or][maxpos] if (ans[order][k]>ans[order][maxpos]) maxpos=k; } } } for (int i=0; i<n; i++){ printf("%d ", orid[i]); for (int j=0; j<3; j++){ int minid=0; for (int k=1; k<3; k++) if (ans[i][k]<ans[i][minid]) minid=k; printf("%d", orid[ansid[i][minid]]); if (j!=2) printf(","); ans[i][minid]=1<<29; } printf("/n"); } return 0; }
F 集合的操作
这道题其实就是经典的集合操作,关键在于如何合并这些操作。
操作最终可以归结为染色和取反两种,分别写好就可以了。
并运算对应的是T部分的染1色。
交运算对应的是非T部分染颜色0,即S中不在T里的部分染成0。
S-T运算就是把T部分染成0色。
对称差运算对应的是T部分中属于S的染成0,不属于S的染成1。这里用到异或/取反。
T-S运算复杂一些:
先把T部分中属于S的染成0,不属于S的染成1。这里用到异或/取反。
再把整区域中非T的部分染成0
这一道题纠结了非常久主要是由于T-S部分的运算没有弄清楚,漏掉了一部分。却一直以为是线段树写的有问题,非常久了才找到问题所在。
#include <cstdio> #include <cstdlib> #include <cstring> const int maxn=65536*3; short c[maxn*4]; short fc[maxn+1]; //final color void print(int k, int l, int r, int x, int y, int newc){ if (x>r || y<l || l>r) return; if (l>=x && r<=y) c[k]=newc; else{ int mid=(l+r)>>1, lson=k<<1, rson=(k<<1)+1; if (c[k]!=-1){ c[lson]=c[rson]=c[k]; c[k]=-1; } print(lson, l, mid, x, y, newc); print(rson, mid+1, r, x, y, newc); if (c[lson]==c[rson] && c[lson]!=-1) c[k]=c[lson]; } } void rev(int k, int l, int r, int x, int y){ if (x>r || y<l || l>r) return; if (l>=x && r<=y && c[k]!=-1) c[k]^=1; else{ int mid=(l+r)>>1, lson=k<<1, rson=(k<<1)+1; if (c[k]!=-1){ c[lson]=c[rson]=c[k]; c[k]=-1; } rev(lson, l, mid, x, y); rev(rson, mid+1, r, x, y); if (c[lson]==c[rson] && c[lson]!=-1) c[k]=c[lson]; } } void update(int k, int l, int r){ if (l==r) fc[l]=c[k]; else{ int mid=(l+r)>>1, lson=(k<<1), rson=(k<<1)+1; if (c[k]!=-1) c[lson]=c[rson]=c[k]; update(lson, l, mid); update(rson, mid+1, r); } } void putans(){ int i=0; bool empty=true; update(1, 0, maxn-1); fc[0]=fc[maxn-1]=0; while (1){ while (fc[i]==0 && i<maxn-1) i++; if (i>=maxn-1) break; if (!empty) printf(" "); printf("%c%d,", (i%3==1)? '[' : '(', i/3); while (fc[i]==1) i++; i--; printf("%d%c", i/3, (i%3==1)? ']' : ')'); empty=false; i++; } if (empty) printf("empty set/n"); else printf("/n"); } int main(){ char mode, lc, rc; int a,b; while (1){ mode=' '; while (mode<'A' || mode>'Z') if ((mode=getchar())==EOF) return 0; memset(c, 0, sizeof(short)*maxn*3); while (mode!='E'){ lc=' '; while (getchar()!=' ') getchar(); scanf("%c%d,%d%c",&lc,&a, &b, &rc); a=(lc=='(') ? a*3+2 : a*3+1; b=(rc==')') ? b*3 : b*3+1; switch(mode){ case 'U': print(1, 0, maxn-1, a, b, 1); break; case 'D': print(1, 0, maxn-1, a, b, 0); break; case 'C': rev(1, 0, maxn-1, a, b); case 'I': print(1, 0, maxn-1, 0, a-1, 0); print(1, 0, maxn-1, b+1, maxn-1, 0); break; case 'S': rev(1, 0, maxn-1, a, b); break; } mode=' '; while (mode<'A' || mode>'Z') mode=getchar(); } putans(); getchar(); getchar(); //"ND" in End } return 0; }
G 网络流
还没有写,大概想了一下构图,但是可能不是最好的。
字符串处理也比较不熟,用STL应该会比较好。
对网络流的算法不是很熟悉,以后找一段时间来突击一下。
H 快速幂
如果能往快速幂方向去想了应该就能比较快得到这个思路,只可惜比赛的时候很二地去推通项,以为能得到一个带有规律的迭代项……
这一道题在long long和int上纠结了比较长时间,太弱了。起初打算偷懒,变量都写成long long的,但是中途发现各种问题,就改掉了。最后得出的结论是,其实int都够用,只是偶尔中间有大数乘积的时候需要用long long暂时放一放再%prime。
long long的输入还是用cin比较靠谱(输出用cout,这题没用到)。还得到一个结果是__int64或者long long最好不要直接和0比较,这一次挂掉了,虽然不知道理论原因,不过知道了可以改写成“if (abs(n-0)<0.5) break;”这样的判断。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <iostream> using namespace std; #define LL long long #define maxn 17 typedef int matrix[maxn][maxn]; void ident(int size, matrix x){ //构造单位阵 for (int i=0; i<size; ++i) for (int j=0; j<size; ++j) x[i][j]=(i==j?1:0); } void copy(int size, matrix x, matrix y){ //矩阵复制 for (int i=0; i<size; ++i) for (int j=0; j<size; ++j) y[i][j]=x[i][j]; } void mutiply(int size, matrix _x, matrix _y, matrix z, LL M){ matrix x, y; copy(size, _x, x); copy(size, _y, y); for (int i=0; i<size; ++i) for (int j=0; j<size; ++j){ z[i][j]=0; for (int k=0; k<size; ++k) z[i][j]=((LL)z[i][j]+(LL)x[i][k]*y[k][j])%M; } } void power(int size, matrix _x, LL n, matrix y, LL M){ matrix x, r; copy(size, _x, x); ident(size, r); while (n){ if (n&1) mutiply(size, r, x, r, M); n>>=1; if (abs(n-0)<0.5) break; mutiply(size, x, x, x, M); } copy(size, r, y); } bool init(int &k, LL &n, LL &m, int &p, matrix a, int b[], int c[]){ if (scanf("%d", &k)!=1) return false; for (int i=0; i<k; i++) scanf("%d", &b[i]); for (int i=0; i<k; i++) scanf("%d", &c[i]); cin>>m>>n>>p; b[k]=0; //b[k]=sumb for (int i=0; i<k; i++) b[k]=(b[k]+b[i])%p; memset(a, 0, sizeof(int)*maxn*maxn); for (int i=0; i<k-1; i++) a[i+1][i]=1; for (int i=0; i<k; i++) a[i][k-1]=a[i][k]=(c[k-i-1])%p; a[k][k]=1; return true; } void getsum(matrix s, int k, int p, int b[], int &sum){ sum=0; for (int i=0; i<k+1; i++) sum=((LL)sum+((LL)b[i]*s[i][k]))%p; } int main(){ matrix a, s; LL n,m; int b[maxn],c[maxn]; int k,sn,sm,p; while (init(k, n, m, p, a, b, c)){ if (n>k){ power(k+1, a, n-k, s, p); getsum(s, k, p, b, sn); } else{ sn=0; for (int i=0; i<n; i++) sn=(sn+b[i])%p; } if (m-1>k){ power(k+1, a, m-k-1, s, p); getsum(s, k, p, b, sm); } else{ sm=0; for (int i=0; i<m-1; i++) sm=(sm+b[i])%p; } printf("%d/n",(sn-sm+p)%p); } return 0; }